package server;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
//一个JSP编译器的实现类
public class DefaultJspCompiler implements JspCompiler {
	
	private StringBuilder jspLine = new StringBuilder();
	//private HashMap<String,String> commandMap = new HashMap<String,String>();
	private String inport="";
	private HashMap<String,String> conmmand = new HashMap<String,String>();
	public File compile(File jspFile, File destPath, String packages, String classPath)  {
		File javaFile;
		try {
			javaFile = compile2java(jspFile, destPath, packages);
			File classFile  = compile2class(javaFile,classPath);
			return classFile;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}
	//将一个Java文件编译成Class文件
	private File compile2class(File javaFile, String classPath) {
		BufferedReader br = null;
		String classFile = javaFile.getPath().substring(0, javaFile.getPath().length()-3)+"class";
		File oldClassFile = new File(javaFile.getPath());
		long modifyTime = 0;
		if(oldClassFile.exists()){
			modifyTime = oldClassFile.lastModified();
		}
		try {
			//利用Runtime.getRuntime().exec执行java命令来编译；为其制定classPath和字符集 和java文件名
			Process p  = Runtime.getRuntime().exec("javac -classpath "+classPath+" -encoding utf-8 "+javaFile.getPath());
			//获取java命令执行的返回字符流
			br = new BufferedReader(new InputStreamReader(p.getErrorStream(),"utf-8"));
			String buff = null;
			while((buff = br.readLine())!=null){
				//仅仅打印到控制台，还没实现反映到用户网页上
				System.out.println(buff);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			if(br!=null){
				try {
					br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		File newClassFile = new File(classFile);
		if(newClassFile.exists()&&newClassFile.lastModified()>modifyTime){
			return newClassFile;
		}
		else{
			return null;
		}
		
		
	}
	//将一个JSP文件解释成Java文件，即一个Servlet
	private File compile2java(File jspFile, File destPath, String packages) throws IOException {

		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(jspFile),"utf8")); 
		parseJsp(reader);
		//目前对JSP命令的实现仅限于import
		if(this.conmmand.get("import")!=null){
			String importString =  this.conmmand.get("import").trim();
			importString = importString.substring(1, importString.length()-1);
			String[] imports =  importString.trim().split(",");
			for(String im : imports){
				this.inport += ("import "+im+";\n");
			}
		}
		
		if(destPath.exists()==false){//如果文件夹不存在，则创建文件夹
			destPath.mkdir();
		}
		String javaFileName = jspFile.getName().substring(0, jspFile.getName().lastIndexOf("."))+"_jsp";
		File javaFile = new File(destPath.getPath()+"/"+javaFileName+".java");
		OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(javaFile), "utf8"); 
		//FileWriter out =new FileWriter(javaFile);
		//包名
		out.write("package "+packages+";\n\n");
		//import
		out.write(this.inport+"\n");
		out.write("import java.io.PrintWriter;\n");
		//类名
		out.write("public class "+javaFileName+" extends servlet.MyServlet {\n");
		out.write("public void service(servlet.MyRequest request,servlet.MyResponse response){\n");
		//内置对象，目前实现了session和out
		out.write("servlet.MySession session = request.getMySession();\n");
		out.write("PrintWriter out = response.getWriter();");
		//将JSP解释的结果加在这里
		out.write(this.jspLine.toString());
		out.write("out.close();\n");
		//结束
		out.write("\n}\n}");
		out.close();
		this.jspLine.setLength(0);
		return javaFile;
	}
	//解析JSP文件，采用的是编译原理中的词法分析器的原理，有穷状态机；（之前画的状态图没留下，--！）
	private void parseJsp(BufferedReader reader) throws IOException{
		StringBuilder sb = new StringBuilder();
		int state = 0;
		int c = 0;
		while(c!=-1){
			switch(state){
				case 0:{
					c = reader.read();
					if((char)c=='"'){
						sb.append("\\\"");
						state = 1;
						break;
					}
					else if((char)c=='<'){
						sb.append((char)c);
						state = 2;
						break;
					}
					else if((char)c=='\\'){
						sb.append("\\\\");
						break;
					}
					else if(c==10){
						sb.append("\\r");
						state = 100;
						break;
					}
					else if(c==13){
						sb.append("\\n");
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
				}
				case 1:{
					c = reader.read();
					if((char)c=='"'){
						sb.append("\\\"");
						state = 0;
						break;
					}
					else if((char)c=='\\'){
						sb.append("\\\\");
						break;
					}
					else if(c==10){
						sb.append("\\r");
						state = 100;
						break;
					}
					else if(c==13){
						sb.append("\\n");
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
					
				}
				case 2:{
					c = reader.read();
					if((char)c=='"'){
						sb.append("\\\"");
						state = 1;
						break;
					}
					else if((char)c=='\\'){
						sb.append("\\\\");
						state = 0;
						break;
					}
					else if(c==10){
						sb.append("\\r");
						state = 100;
						break;
					}
					else if(c==13){
						sb.append("\\n");
						state = 0;
						break;
					}
					else if((char)c=='%'){
						sb.setLength(sb.length()-1);
						appendStaticCode(sb.toString());
						sb.setLength(0);
						state = 3;
						break;
					}
					else{
						sb.append((char)c);
						state = 0;
						break;
					}
				}
				case 3:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 6;
						break;
					}
					else if((char)c=='@'){
						state = 4;
						break;
					}
					else if((char)c=='%'){
						sb.append((char)c);
						state = 10;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						state = 5;
						break;
					}
					
				}
				case 4:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 7;
						break;
					}
					else if((char)c=='%'){
						sb.append((char)c);
						state = 9;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
				}
				case 5:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 8;
						break;
					}
					else if((char)c=='%'){
						sb.append((char)c);
						state = 10;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
				}
				case 6:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 3;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
				}
				case 7:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 4;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
				}
				case 8:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 5;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						break;
					}
					
				}
				case 9:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 7;
						break;
					}
					else if((char)c=='>'){
						state = 200;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						state = 4;
						break;
					}
					
				}
				case 10:{
					c = reader.read();
					if((char)c=='"'){
						sb.append((char)c);
						state = 8;
						break;
					}
					else if((char)c=='>'){
						state = 300;
						break;
					}
					else if(c==10){
						sb.append((char)c);
						break;
					}
					else if(c==13){
						sb.append((char)c);
						break;
					}
					else{
						sb.append((char)c);
						state = 5;
						break;
					}
				}
				case 100:{
					appendStaticCode(sb.toString());
					sb.setLength(0);
					state = 0;
					break;
				}
				case 200:{
					sb.setLength(sb.length()-1);
					appendJspConmmand(sb.toString());
					sb.setLength(0);
					state = 0;
					break;
				}
				case 300:{
					sb.setLength(sb.length()-1);
					appendJspCode(sb.toString());
					sb.setLength(0);
					state = 0;
					break;
				}

				
			}
		}
	}
	//将JSP解释成Java使用到的，将静态的HTML代码转换为out.print(\""+staticCode+"\");
	private void appendStaticCode(String staticCode) {
		jspLine.append("out.print(\""+staticCode+"\");\n");
	}
	//JSP命令解析，将解析的结果放入HashMap，conmmand中
	private void appendJspConmmand(String jspConmmand) {
		jspConmmand = jspConmmand.trim();
		String[] tokens = jspConmmand.split(" ");
		for(int i=0;i<tokens.length;i++){
			String token =tokens[i].trim();
			//System.out.println(token);
			if(token.contains("=")){
				String[] pair = token.split("=");
				conmmand.put(pair[0].trim(), pair[1].trim());				
			}

		}
		
	}
	//将JSP标签内的代码直接作为Java文件的代码
	private void appendJspCode(String jspCode) {
		jspLine.append(jspCode+"\n");
		
	}
	//一个测试用的main方法
	public static void main(String[] args){
		File jsp = new File("c:/a.jsp");
		File path = new File("c:/jspPath/");
		//System.out.println(jsp.getParent());
		new DefaultJspCompiler().compile(jsp, path,"jspPath",".;C:/jspPath;");
		
		BufferedReader br = null;
		try {
			Process p  = Runtime.getRuntime().exec("javac -classpath .;C:/jspPath; c:/jspPath/a_jsp.java");
			br = new BufferedReader(new InputStreamReader(p.getErrorStream(),"utf-8"));
			String buff = null;
			while((buff = br.readLine())!=null){
				System.out.println(buff);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			if(br!=null){
				try {
					br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}
